package engine;

import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.util.ArrayList;

import utils.SortedList;

public abstract class Room extends Spritable implements RoomRO {

	private String name;
	private Dimension size;
	private final SortedList<Actor> actors = new SortedList<Actor>();
	private BufferedImage view;
	private boolean persistent, runsInBackground;

	// Constructors

	public Room(int width, int height) {
		setSize(new Dimension(width, height));
		init();
	}

	public Room(String name, int width, int height) {
		this(width, height);
		this.name = name;
	}

	private final void init() {
		actors.clear();
		initialize();
	}

	protected abstract void initialize();

	public final void restart() {
		init();
	}

	// Destructor

	@Override
	protected void finalize() throws Throwable {
		close();
	}

	protected abstract void close();

	// Name

	protected void setName(String name) {
		this.name = name;
	}

	public String getName() {
		return name;
	}

	// Game

	synchronized void step(float fpsFactor) {
		stepAll(fpsFactor);
		collideAll();
		paintAll((Graphics2D) view.getGraphics());
	}

	private synchronized void stepAll(float fpsFactor) {
		super.step(fpsFactor);
		for (int i = 0; i < actors.size(); i++) {
			Actor actor = actors.get(i);
			actor.step(this, fpsFactor);
			for (Actor a : actor.getSubActors())
				a.step(this, fpsFactor);
		}
	}

	private synchronized void collideAll() {
		for (int i = 0; i < actors.size(); i++)
			for (int j = i + 1; j < actors.size(); j++)
				collide(actors.get(i), actors.get(j));
	}

	private synchronized void collide(Actor a1, Actor a2) {
		if (!a1.intersects(a2))
			return;
		a1.collide(a2);
		a2.collide(a1);
	}

	private synchronized void paintAll(Graphics2D g) {
		g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
		// background
		if (getSpr() != null)
			for (int i = 0; i <= getWidth() + getSpr().getWidth(); i += getSpr().getWidth())
				for (int j = 0; j <= getHeight() + getSpr().getWidth(); j += getSpr().getHeight())
					paint(g, i, j);
		// actors
		for (int i = 0; i < actors.size(); i++) {
			Actor actor = actors.get(i);
			actor.paint(g);
			for (Actor a : actor.getSubActors())
				a.paint(g);
		}
	}

	public BufferedImage getView() {
		return view;
	}

	public ArrayList<Actor> getActors() {
		return actors;
	}

	public void addActor(Actor a) {
		actors.add(a);
	}

	public void addActor(Actor a, float x, float y) {
		actors.add(a);
		a.setXPos(x);
		a.setYPos(y);
	}

	// Setters and Getters

	public Dimension getSize() {
		return size;
	}

	public float getWidth() {
		return (float) size.getWidth();
	}

	public float getHeight() {
		return (float) size.getHeight();
	}

	public boolean isPersistent() {
		return persistent;
	}

	public boolean isRunsInBackground() {
		return runsInBackground;
	}

	public void setSize(Dimension size) {
		this.size = size;
		view = new BufferedImage((int) size.getWidth(), (int) size.getHeight(), BufferedImage.TYPE_INT_ARGB);
	}

	public void setPersistent(boolean isPersistent) {
		this.persistent = isPersistent;
		this.runsInBackground = isPersistent && this.runsInBackground;
	}

	public void setRunsInBackground(boolean isRunsInBackground) {
		this.runsInBackground = isRunsInBackground;
		this.persistent = isRunsInBackground || this.persistent;
	}

	// STRINGS

	@Override
	public String toString() {
		return name + ": " + actors;
	}

}